package com.faxsun.core.order.service;

import com.faxsun.common.currency.dao.FSCurrencyRateDao;
import com.faxsun.common.currency.domain.FSCurrencyEnum;
import com.faxsun.common.currency.domain.FSCurrencyRate;
import com.faxsun.core.catalog.domain.FSProductImpl;
import com.faxsun.core.catalog.domain.FSSkuImpl;
import com.faxsun.core.order.dao.FSOrderItemDao;
import com.faxsun.core.order.domain.FSOrderItemImpl;
import com.faxsun.profile.core.domain.FSReferralCustomerOrderItemXref;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.broadleafcommerce.common.currency.domain.BroadleafCurrency;
import org.broadleafcommerce.common.util.BLCSystemProperty;
import org.broadleafcommerce.core.catalog.domain.Category;
import org.broadleafcommerce.core.catalog.domain.Product;
import org.broadleafcommerce.core.catalog.domain.ProductOption;
import org.broadleafcommerce.core.catalog.domain.Sku;
import org.broadleafcommerce.core.catalog.service.dynamic.DynamicSkuPrices;
import org.broadleafcommerce.core.catalog.service.dynamic.DynamicSkuPricingService;
import org.broadleafcommerce.core.order.dao.OrderItemDao;
import org.broadleafcommerce.core.order.domain.*;
import org.broadleafcommerce.core.order.service.OrderItemServiceImpl;
import org.broadleafcommerce.core.order.service.call.AbstractOrderItemRequest;
import org.broadleafcommerce.core.order.service.call.DiscreteOrderItemRequest;
import org.broadleafcommerce.core.order.service.call.GiftWrapOrderItemRequest;
import org.broadleafcommerce.core.order.service.type.OrderItemType;
import org.broadleafcommerce.core.order.service.type.OrderStatus;
import org.broadleafcommerce.profile.core.domain.Customer;

import javax.annotation.Resource;
import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * 重写部分函数，将OrderItem中的销售价格改为海外价格*汇率。原因：汇率每天都可能发生变化
 *
 * @author zhaoteng.song@faxsun.com
 */

public class FSOrderItemServiceImpl extends OrderItemServiceImpl {

    private static final Log LOG = LogFactory.getLog(FSOrderItemServiceImpl.class);
    @Resource(name = "fsCurrencyRateDao")
    protected FSCurrencyRateDao currencyRateDao;
    @Resource(name = "blOrderItemDao")
    protected OrderItemDao orderItemdao;
    @Resource(name = "currencySkuPricingService")
    DynamicSkuPricingService currencyPricingServicee;

    @SuppressWarnings("deprecation")
    @Override
    public OrderItem updateDiscreteOrderItem(OrderItem item, final DiscreteOrderItemRequest itemRequest) {
        List<ProductOption> productOptions = null;
        if (item instanceof DiscreteOrderItem) {
            productOptions = ((DiscreteOrderItem) item).getProduct().getProductOptions();
        } else if (item instanceof BundleOrderItem) {
            productOptions = ((BundleOrderItem) item).getProduct().getProductOptions();
        }
        List<String> removeKeys = new ArrayList<String>();
        if (productOptions != null && itemRequest.getItemAttributes() != null) {
            for (String name : itemRequest.getItemAttributes().keySet()) {
                // we do not let them update all product options.
                // Only allow them to update those options that can have
                // validation to take place at later time
                // if option.getProductOptionValidationType() is null then it
                // might change the sku, so we dont allow those
                for (ProductOption option : productOptions) {
                    if (option.getAttributeName().equals(name)
                        && option.getProductOptionValidationStrategyType() == null) {

                        removeKeys.add(name);
                        break;
                    }
                }
            }
        }
        for (String name : removeKeys) {
            itemRequest.getItemAttributes().remove(name);
        }
        populateProductOptionAttributes(item, itemRequest.getItemAttributes());
        this.updateItemPrice(item, itemRequest.getSku());
        return item;
    }

    @Override
    public DiscreteOrderItem createDiscreteOrderItem(final DiscreteOrderItemRequest itemRequest) {
        final DiscreteOrderItem item = (DiscreteOrderItem) orderItemDao.create(OrderItemType.DISCRETE);
        populateDiscreteOrderItem(item, itemRequest);

        item.setBundleOrderItem(itemRequest.getBundleOrderItem());
        item.setBaseSalePrice(itemRequest.getSalePriceOverride() == null ? itemRequest.getSku()
            .getSalePrice() : itemRequest.getSalePriceOverride());
        item.setBaseRetailPrice(itemRequest.getSku().getRetailPrice());
        item.setDiscreteOrderItemFeePrices(itemRequest.getDiscreteOrderItemFeePrices());

        if (itemRequest.getSalePriceOverride() != null) {
            item.setSalePriceOverride(Boolean.TRUE);
            item.setSalePrice(itemRequest.getSalePriceOverride());
            item.setBaseSalePrice(itemRequest.getSalePriceOverride());
        }

        if (itemRequest.getRetailPriceOverride() != null) {
            item.setRetailPriceOverride(Boolean.TRUE);
            item.setRetailPrice(itemRequest.getRetailPriceOverride());
            item.setBaseRetailPrice(itemRequest.getRetailPriceOverride());
        }

        for (DiscreteOrderItemFeePrice feePrice : item.getDiscreteOrderItemFeePrices()) {
            feePrice.setDiscreteOrderItem(item);
        }

        item.setPersonalMessage(itemRequest.getPersonalMessage());
        this.updateItemPrice(item, itemRequest.getSku());

        return item;
    }

    public DiscreteOrderItem createDiscreteOrderItem(final AbstractOrderItemRequest itemRequest) {
        final DiscreteOrderItem item = (DiscreteOrderItem) orderItemDao.create(OrderItemType.DISCRETE);
        populateDiscreteOrderItem(item, itemRequest);
        item.setBaseSalePrice(itemRequest.getSku().getSalePrice());
        item.setBaseRetailPrice(itemRequest.getSku().getRetailPrice());
        // item.updatePrices();
        item.updateSaleAndRetailPrices();

        item.assignFinalPrice();
        item.setPersonalMessage(itemRequest.getPersonalMessage());
        this.updateItemPrice(item, itemRequest.getSku());

        return item;
    }

    @Override
    public DiscreteOrderItem createDynamicPriceDiscreteOrderItem(final DiscreteOrderItemRequest itemRequest,
                                                                 @SuppressWarnings("rawtypes") HashMap skuPricingConsiderations) {
        final DiscreteOrderItem item = (DiscreteOrderItem) orderItemDao
            .create(OrderItemType.EXTERNALLY_PRICED);
        populateDiscreteOrderItem(item, itemRequest);

        DynamicSkuPrices prices = dynamicSkuPricingService.getSkuPrices(itemRequest.getSku(),
            skuPricingConsiderations);
        item.setBundleOrderItem(itemRequest.getBundleOrderItem());
        item.setBaseRetailPrice(prices.getRetailPrice());
        item.setBaseSalePrice(prices.getSalePrice());
        item.setSalePrice(prices.getSalePrice());
        item.setRetailPrice(prices.getRetailPrice());

        if (itemRequest.getSalePriceOverride() != null) {
            item.setSalePriceOverride(Boolean.TRUE);
            item.setSalePrice(itemRequest.getSalePriceOverride());
            item.setBaseSalePrice(itemRequest.getSalePriceOverride());
        }

        if (itemRequest.getRetailPriceOverride() != null) {
            item.setRetailPriceOverride(Boolean.TRUE);
            item.setRetailPrice(itemRequest.getRetailPriceOverride());
            item.setBaseRetailPrice(itemRequest.getRetailPriceOverride());
        }

        item.setDiscreteOrderItemFeePrices(itemRequest.getDiscreteOrderItemFeePrices());
        for (DiscreteOrderItemFeePrice fee : itemRequest.getDiscreteOrderItemFeePrices()) {
            item.setSalePrice(item.getSalePrice().add(fee.getAmount()));
            item.setRetailPrice(item.getRetailPrice().add(fee.getAmount()));
        }

        item.setPersonalMessage(itemRequest.getPersonalMessage());
        this.updateItemPrice(item, itemRequest.getSku());

        return item;
    }

    @Override
    public GiftWrapOrderItem createGiftWrapOrderItem(final GiftWrapOrderItemRequest itemRequest) {
        final GiftWrapOrderItem item = (GiftWrapOrderItem) orderItemDao.create(OrderItemType.GIFTWRAP);
        item.setSku(itemRequest.getSku());
        item.setOrder(itemRequest.getOrder());
        item.setBundleOrderItem(itemRequest.getBundleOrderItem());
        item.setQuantity(itemRequest.getQuantity());
        item.setCategory(itemRequest.getCategory());
        item.setProduct(itemRequest.getProduct());
        item.setBaseSalePrice(itemRequest.getSku().getSalePrice());
        item.setBaseRetailPrice(itemRequest.getSku().getRetailPrice());
        item.setDiscreteOrderItemFeePrices(itemRequest.getDiscreteOrderItemFeePrices());

        if (itemRequest.getSalePriceOverride() != null) {
            item.setSalePriceOverride(Boolean.TRUE);
            item.setSalePrice(itemRequest.getSalePriceOverride());
            item.setBaseSalePrice(itemRequest.getSalePriceOverride());
        }

        if (itemRequest.getRetailPriceOverride() != null) {
            item.setRetailPriceOverride(Boolean.TRUE);
            item.setRetailPrice(itemRequest.getRetailPriceOverride());
            item.setBaseRetailPrice(itemRequest.getRetailPriceOverride());
        }

        // item.updatePrices();
        item.updateSaleAndRetailPrices();
        item.assignFinalPrice();
        item.getWrappedItems().addAll(itemRequest.getWrappedItems());
        for (OrderItem orderItem : item.getWrappedItems()) {
            orderItem.setGiftWrapOrderItem(item);
        }
        this.updateItemPrice(item, itemRequest.getSku());

        return item;
    }

    // @Override
    // public BundleOrderItem createBundleOrderItem(
    // final BundleOrderItemRequest itemRequest) {
    // final BundleOrderItem item = (BundleOrderItem) orderItemDao
    // .create(OrderItemType.BUNDLE);
    // item.setQuantity(itemRequest.getQuantity());
    // item.setCategory(itemRequest.getCategory());
    // item.setName(itemRequest.getName());
    // item.setBundleOrderItemFeePrices(itemRequest
    // .getBundleOrderItemFeePrices());
    // item.setOrder(itemRequest.getOrder());
    //
    // if (itemRequest.getSalePriceOverride() != null) {
    // item.setSalePriceOverride(Boolean.TRUE);
    // item.setSalePrice(itemRequest.getSalePriceOverride());
    // item.setBaseSalePrice(itemRequest.getSalePriceOverride());
    // }
    //
    // if (itemRequest.getRetailPriceOverride() != null) {
    // item.setRetailPriceOverride(Boolean.TRUE);
    // item.setRetailPrice(itemRequest.getRetailPriceOverride());
    // item.setBaseRetailPrice(itemRequest.getRetailPriceOverride());
    // }
    //
    // for (DiscreteOrderItemRequest discreteItemRequest : itemRequest
    // .getDiscreteOrderItems()) {
    // discreteItemRequest.setBundleOrderItem(item);
    // DiscreteOrderItem discreteOrderItem;
    // if (discreteItemRequest instanceof GiftWrapOrderItemRequest) {
    // discreteOrderItem = createGiftWrapOrderItem((GiftWrapOrderItemRequest)
    // discreteItemRequest);
    // } else {
    // discreteOrderItem = createDiscreteOrderItem(discreteItemRequest);
    // }
    // item.getDiscreteOrderItems().add(discreteOrderItem);
    // }
    //
    // return item;
    // }
    //
    // @Override
    // public BundleOrderItem createBundleOrderItem(
    // final ProductBundleOrderItemRequest itemRequest, boolean saveItem) {
    // ProductBundle productBundle = itemRequest.getProductBundle();
    // BundleOrderItem bundleOrderItem = (BundleOrderItem) orderItemDao
    // .create(OrderItemType.BUNDLE);
    // bundleOrderItem.setQuantity(itemRequest.getQuantity());
    // bundleOrderItem.setCategory(itemRequest.getCategory());
    // bundleOrderItem.setSku(itemRequest.getSku());
    // bundleOrderItem.setName(itemRequest.getName());
    // bundleOrderItem.setProductBundle(productBundle);
    // bundleOrderItem.setOrder(itemRequest.getOrder());
    //
    // if (itemRequest.getSalePriceOverride() != null) {
    // bundleOrderItem.setSalePriceOverride(Boolean.TRUE);
    // bundleOrderItem.setSalePrice(itemRequest.getSalePriceOverride());
    // bundleOrderItem
    // .setBaseSalePrice(itemRequest.getSalePriceOverride());
    // }
    //
    // if (itemRequest.getRetailPriceOverride() != null) {
    // bundleOrderItem.setRetailPriceOverride(Boolean.TRUE);
    // bundleOrderItem
    // .setRetailPrice(itemRequest.getRetailPriceOverride());
    // bundleOrderItem.setBaseRetailPrice(itemRequest
    // .getRetailPriceOverride());
    // }
    //
    // for (SkuBundleItem skuBundleItem : productBundle.getSkuBundleItems()) {
    // Product bundleProduct = skuBundleItem.getBundle();
    // Sku bundleSku = skuBundleItem.getSku();
    //
    // Category bundleCategory = null;
    // if (itemRequest.getCategory() != null) {
    // bundleCategory = itemRequest.getCategory();
    // }
    //
    // if (bundleCategory == null && bundleProduct != null) {
    // bundleCategory = bundleProduct.getDefaultCategory();
    // }
    //
    // DiscreteOrderItemRequest bundleItemRequest = new
    // DiscreteOrderItemRequest();
    // bundleItemRequest.setCategory(bundleCategory);
    // bundleItemRequest.setProduct(bundleProduct);
    // bundleItemRequest.setQuantity(skuBundleItem.getQuantity());
    // bundleItemRequest.setSku(bundleSku);
    // bundleItemRequest
    // .setItemAttributes(itemRequest.getItemAttributes());
    // bundleItemRequest
    // .setSalePriceOverride(skuBundleItem.getSalePrice());
    // bundleItemRequest.setBundleOrderItem(bundleOrderItem);
    //
    // DiscreteOrderItem bundleDiscreteItem =
    // createDiscreteOrderItem(bundleItemRequest);
    // bundleDiscreteItem.setSkuBundleItem(skuBundleItem);
    // bundleOrderItem.getDiscreteOrderItems().add(bundleDiscreteItem);
    // }
    //
    // if (saveItem) {
    // bundleOrderItem = (BundleOrderItem) saveOrderItem(bundleOrderItem);
    // }
    //
    // return bundleOrderItem;
    // }

    /**
     * 根据汇率更新价格,并记录当前汇率
     *
     * @param item
     * @param sku
     */
    protected void updateItemPrice(OrderItem item, Sku sku) {
        try {
            FSOrderItemImpl orderItem = (FSOrderItemImpl) item;
            FSSkuImpl skuImpl = (FSSkuImpl) sku;

            DynamicSkuPrices prices = this.dynamicSkuPricingService.getSkuPrices(skuImpl, null);

            BroadleafCurrency source = skuImpl.getPriceBroadleafCurrency();
            BroadleafCurrency dest = FSCurrencyEnum.parse("CNY");
            FSCurrencyRate rate = currencyRateDao.readByCodes(source, dest);

            if (prices == null) {
                throw new Exception("Get Sku Prices Failed!!!");
            } else {
                if (!item.getRetailPrice().equals(prices.getRetailPrice()))
                    item.setRetailPrice(prices.getRetailPrice());
                else
                    LOG.debug("Retail Prices has no changes");
                if (!item.getSalePrice().equals(prices.getSalePrice()))
                    item.setSalePrice(prices.getSalePrice());
                else
                    LOG.debug("Sale Prices has no changes");

                // record currency and rate
                // TODO: 将汇率放入内存或内存数据库中，直接读取
                orderItem.setCurrencyType(skuImpl.getPriceBroadleafCurrency());
                orderItem.setCurrencyRate(rate.getRate());
                orderItem.setForiegnPrice(skuImpl.getForeignPrice().getAmount());
            }
        } catch (Exception e) {
            LOG.error(e.getLocalizedMessage());
        }
    }

    public Long findOrderItemQuantitiesByCustomerAndCategory(Customer customer, Category category) {
        return (orderItemdao instanceof FSOrderItemDao) ? ((FSOrderItemDao) orderItemdao)
            .readOrderItemQuantitiesByCustomerAndCategory(customer, category) : null;
    }

    public List<OrderItem> findOrderItemsByCustomerAndCategory(Customer customer, Category category) {
        return (orderItemdao instanceof FSOrderItemDao) ? ((FSOrderItemDao) orderItemdao)
            .readOrderItemsByCustomerAndCategory(customer, category) : null;
    }

    public List<OrderItem> readReFoundOrderItemsByCustomer(Customer customer) {
        return (orderItemdao instanceof FSOrderItemDao) ? ((FSOrderItemDao) orderItemdao)
            .readReFoundOrderItemsByCustomer(customer) : null;
    }

    public List<FSReferralCustomerOrderItemXref> readReferralOrderItemsByCustomer(Customer customer) {
        return (orderItemdao instanceof FSOrderItemDao) ? ((FSOrderItemDao) orderItemdao)
            .readReferralOrderItemsByCustomer(customer) : null;
    }

    public BigDecimal calculateOrderItemReferralCredit(OrderItem orderItem) {
        BigDecimal referralCredit = BigDecimal.ZERO;
        if (orderItem instanceof FSOrderItemImpl) {
            FSOrderItemImpl fsOrderItem = (FSOrderItemImpl) orderItem;

            if (fsOrderItem.getProduct() instanceof FSProductImpl) {
                FSProductImpl fsProduct = (FSProductImpl) fsOrderItem.getProduct();
                String referralRatio;

                if (fsProduct.getProductAttributes().containsKey(FSReferralCustomerOrderItemConstants.REFERRAL_RATIO)) {
                    referralRatio = fsProduct.getProductAttributes().get(
                        FSReferralCustomerOrderItemConstants.REFERRAL_RATIO).toString();
                } else {
                    referralRatio = fsProduct.getIsBroad() ?
                        BLCSystemProperty.resolveSystemProperty(
                            FSReferralCustomerOrderItemConstants.REFERRAL_RATIO_FOREIGN, "0") :
                        BLCSystemProperty.resolveSystemProperty(
                            FSReferralCustomerOrderItemConstants.REFERRAL_RATIO_DOMESTIC, "0");
                }

                referralCredit = new BigDecimal(referralRatio)
                    .multiply((fsOrderItem).getProduct().getDefaultSku().getPrice()
                        .add(fsOrderItem.getSingleCalculatedShippmentFee()).getAmount())
                    .multiply(BigDecimal.valueOf(fsOrderItem.getQuantity()));
            }
        }
        return referralCredit;
    }

    public Map<Long, Long> readOrderItemQuantitiesByProductsAndStatus(List<Product> products, List<String> orderStatuses) {
        return (orderItemdao instanceof FSOrderItemDao) ? ((FSOrderItemDao) orderItemdao)
            .readOrderItemQuantitiesByProductsAndStatus(products, orderStatuses) : null;
    }

}
